﻿/*****************************************************
 * 本文件来自 http://www.cnblogs.com/youzai/ 优哉@游哉 的博客
 * 作者：优哉@游哉
 * 略有修改，增加%支持
 * 
 *****************************************************/
using System;
using System.Collections.Generic;
using System.Text;

namespace JinianNet.JNTemplate.Process.Common {
    public class CalculatorHelper {
        public static bool Calculate(string infixExpression, out float result,

           out string errorExpression) {

            result = 0;

            errorExpression = string.Empty;

            infixExpression = infixExpression.Replace(" ", string.Empty);

            if (infixExpression.EndsWith("=")) {

                infixExpression = infixExpression.Substring(0, infixExpression.Length - 1);

            }

            IList<char> validCharList = new List<char>();

            for (char ch = '0'; ch <= '9'; ch++) {

                validCharList.Add(ch);

            }

            validCharList.Add('.');

            validCharList.Add('e');

            validCharList.Add('E');

            validCharList.Add('+');

            validCharList.Add('-');

            validCharList.Add('*');

            validCharList.Add('/');

            validCharList.Add('^');

            validCharList.Add('%');

            validCharList.Add('(');

            validCharList.Add(')');

            foreach (char ch in infixExpression) {

                if (!validCharList.Contains(ch)) {

                    errorExpression = "表达式包含非法字符";

                    return false;

                }

            }


            for (int i = 0; i < infixExpression.Length; i++) {

                char ch = infixExpression[i];

                if (ch == '^') {

                    int j = i + 1;

                    if (j < infixExpression.Length) {

                        char nextChar = infixExpression[j];

                        if (nextChar == '-') {

                            infixExpression = infixExpression.Insert(j, "(0");

                            i += 2;

                            j += 2;

                            int k = j + 1;

                            for (; k < infixExpression.Length; k++) {

                                if (!char.IsNumber(infixExpression[k]) &&

                                    infixExpression[k] != '.') {

                                    break;

                                }

                            }

                            infixExpression = infixExpression.Insert(k, ")");

                        }

                    }

                }

            }

            Queue<string> postfixQueue = GetPostfixQueue(infixExpression);

            Stack<float> stackOperand = new Stack<float>();

            while (postfixQueue.Count != 0) {

                string expression = postfixQueue.Dequeue();

                if (IsOperator(expression)) {

                    if (stackOperand.Count < 2) {

                        errorExpression = "表达式语法错误";

                        return false;

                    }

                    float operand1 = stackOperand.Pop();

                    float operand2 = stackOperand.Pop();

                    float resultTmp;

                    if (Calculate(operand2, operand1, expression, out resultTmp,

                        out errorExpression)) {

                        stackOperand.Push(resultTmp);

                    }
                    else {

                        return false;

                    }

                }
                else {

                    float resultTmp;

                    if (float.TryParse(expression, out resultTmp)) {

                        stackOperand.Push(resultTmp);

                    }
                    else {

                        errorExpression = string.Format("无法转换{0} 为数字", resultTmp);

                        return false;

                    }

                }

            }



            if (stackOperand.Count == 1) {

                result = stackOperand.Pop();

                return true;

            }



            errorExpression = "表达式语法错误";

            return false;

        }



        private static bool Calculate(float operand1, float operand2, string expression,

            out float result, out string errorExpression) {

            result = 0;

            errorExpression = string.Empty;

            if (expression == "+") {

                result = operand1 + operand2;

            }
            else if (expression == "-") {

                result = operand1 - operand2;

            }
            else if (expression == "*") {

                result = operand1 * operand2;

            }
            else if (expression == "%") {

                result = operand1 % operand2;

            }
            else if (expression == "/") {

                if (operand2 == 0) {

                    errorExpression = "除数不能为零";

                    return false;

                }

                result = operand1 / operand2;

            }
            else if (expression == "^") {

                result = (float)Math.Pow(Convert.ToDouble(operand1), Convert.ToDouble(operand2));

            }

            return true;

        }



        private static Queue<string> GetPostfixQueue(string infixExpression) {

            Queue<string> postfixQueue = new Queue<string>();

            Stack<char> stack = new Stack<char>();

            if (infixExpression[0] == '-') {

                infixExpression = "0" + infixExpression;

            }



            IList<char> validCharList = new List<char>();

            for (char i = '0'; i <= '9'; i++) {

                validCharList.Add(i);

            }

            validCharList.Add('.');

            validCharList.Add('e');

            validCharList.Add('E');

            while (infixExpression != string.Empty) {

                char ch = infixExpression[0];



                if (validCharList.Contains(ch)) {

                    string oprand = string.Empty;

                    while (validCharList.Contains(infixExpression[0])) {

                        oprand += infixExpression[0].ToString();

                        infixExpression = infixExpression.Substring(1);

                        if (infixExpression == string.Empty) {

                            break;

                        }

                    }

                    postfixQueue.Enqueue(oprand);

                }
                else if (IsOperator(ch.ToString())) {

                    if (stack.Count > 0) {

                        char chOperator = stack.Peek();

                        while (!(chOperator == '(' || ComparePriority(chOperator, ch) < 0 ||

                            (ComparePriority(chOperator, ch) == 0 && chOperator == '^')) &&

                            stack.Count > 0) {

                            char chOther = stack.Pop();

                            postfixQueue.Enqueue(chOther.ToString());

                            if (stack.Count < 1) {

                                break;

                            }

                            chOperator = stack.Peek();

                        }

                    }

                    stack.Push(ch);

                    infixExpression = infixExpression.Substring(1);

                }
                else if (ch == '(') {

                    stack.Push(ch);

                    infixExpression = infixExpression.Substring(1);

                }
                else if (ch == ')') {

                    char chOperator = stack.Pop();

                    while (chOperator != '(' && stack.Count > 0) {

                        postfixQueue.Enqueue(chOperator.ToString());

                        chOperator = stack.Pop();

                    }

                    infixExpression = infixExpression.Substring(1);

                }

            }

            while (stack.Count > 0) {

                char ch = stack.Pop();

                if (ch != '(') {

                    postfixQueue.Enqueue(ch.ToString());

                }

            }

            return postfixQueue;

        }



        private static int ComparePriority(char chOperator, char otherOperator) {

            IDictionary<char, int> dictionary = new Dictionary<char, int>();

            dictionary.Add('+', 0);

            dictionary.Add('-', 0);

            dictionary.Add('*', 1);

            dictionary.Add('/', 1);

            dictionary.Add('^', 2);

            dictionary.Add('%', 1);


            if (dictionary.ContainsKey(chOperator) &&

                dictionary.ContainsKey(otherOperator)) {

                return dictionary[chOperator] - dictionary[otherOperator];

            }



            throw new NotSupportedException(string.Format("Not supported operator: {0}",
                otherOperator));

        }



        private static bool IsOperator(string str) {

            IList<string> list = new List<string>(new string[]{"+","-","*","/","^","%"});
            return list.Contains(str);

        }
    }
}
